using System;
using System.Diagnostics;
using Mbs.Collections;
using Mbs.ComponentModel.Container.Activation;
using Mbs.ComponentModel.Container.Context;
using Mbs.ComponentModel.Container.Lifestyle;
using Mbs.Reflection;

namespace Mbs.ComponentModel.Container.Metadata
{
    [Serializable]
    [DebuggerDisplay("Id")]
    public class ComponentInfo : IComponentInfo
    {
        public string Id { get; private set; }
        public Type Service { get; private set; }
        public Type Implementation { get; private set; }
        public bool IsLazyInit { get; set; }
        public string Lifestyle { get; set; }

        public string Activator { get; set; }
        public Func<ICreateContext, object> Factory { get; internal set; }

        public IPropertySet ExtendedProperties { get; private set; }

        private readonly int HashCode;
        private string Name;

        public ComponentInfo(string id, Type service, Func<ICreateContext, object> factory, string lifestyleType, bool isLazyInit)
        {
            Trace.Assert(service != null, "service == null");
            Trace.Assert(factory != null, "factory == null");

            Id = id;
            if (string.IsNullOrEmpty(id))
                Id = factory.ToString() + "/" + service.FullName;

            Lifestyle = lifestyleType;
            if (string.IsNullOrEmpty(lifestyleType))
                Lifestyle = LifestyleType.Singleton;

            if (service.IsOpenGenericType())
                Lifestyle = GetGenericLifestyle(Lifestyle);

            Activator = ActivatorType.Factory;
            Factory = factory;
            Name = Id;
            Service = service;
            IsLazyInit = isLazyInit;
            ExtendedProperties = new PropertySet(StringComparer.OrdinalIgnoreCase);
            HashCode = Id.GetHashCode();
        }

        public ComponentInfo(string id, Type service, Func<ICreateContext, object> factory) : this(id, service, factory, LifestyleType.Singleton, true) { }

        public ComponentInfo(Type service, Func<ICreateContext, object> factory) : this(null, service, factory) { }

        public ComponentInfo(string id, Type service, Type implementation,string activator, string lifestyleType, bool isLazyInit)
        {
           
            Trace.Assert(implementation != null, "implementation == null");
            Trace.Assert(!implementation.IsAbstract, "implementation is abstract");
            Trace.Assert(!implementation.IsInterface, "implementation is interface");

            if (service == null)
                service = implementation;

            if (!service.IsGenericType || service.IsCloseGenericType())
                Trace.Assert(service.IsAssignableFrom(implementation), "service.IsAssignableFrom(implementation)");

            Id = id;
            if (string.IsNullOrEmpty(id))
                Id = implementation.FullName;

            Lifestyle = lifestyleType;
            if (string.IsNullOrEmpty(lifestyleType))
                Lifestyle = LifestyleType.Singleton;

            if (service.IsOpenGenericType())
                Lifestyle = GetGenericLifestyle(Lifestyle);

            Activator = activator;
            if (string.IsNullOrEmpty(activator))
                Activator = ActivatorType.Default;


            Name = Id;
            Service = service;
            Implementation = implementation;
            IsLazyInit = isLazyInit;
            ExtendedProperties = new PropertySet(StringComparer.OrdinalIgnoreCase);
            HashCode = Service.GetHashCode() ^ Lifestyle.GetHashCode();
            if (implementation != null)
            {
                HashCode = HashCode ^ implementation.GetHashCode();
                Name = implementation.FullName + "/" + service.FullName;
            }
        }

        public ComponentInfo(string id, Type service, Type implementation) : this(id, service, implementation, ActivatorType.Default, LifestyleType.Singleton, true) { }

        public ComponentInfo(Type service, Type implementation) : this(null, service, implementation) { }

        private static string GetGenericLifestyle(string lifestyle)
        {
            if (string.Equals(lifestyle, LifestyleType.Singleton, StringComparison.OrdinalIgnoreCase))
                return LifestyleType.GenericSingleton;
            if (string.Equals(lifestyle, LifestyleType.Transient, StringComparison.OrdinalIgnoreCase))
                return LifestyleType.GenericTransient;
            return lifestyle;
        }

       


        public override int GetHashCode()
        {
            return HashCode;
        }


        public static bool operator ==(ComponentInfo left, ComponentInfo right)
        {
            if (left != null)
                return left.Equals(right);
            return false;
        }


        public static bool operator !=(ComponentInfo left, ComponentInfo right)
        {
            return !(left == right);
        }


        public override string ToString()
        {
            return Name;
        }

        public override bool Equals(object obj)
        {
            return Equals((IComponentInfo)obj);
        }

        public bool Equals(IComponentInfo other)
        {
            if (other == null)
                return false;
            if (Implementation != other.Implementation)
                return false;
            if (Service != other.Service)
                return false;
            return true;
        }
    }
}
